package com.google.code.openperfmon.service;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.exception.ConstraintViolationException;

import com.google.code.openperfmon.dao.HostDAO;
import com.google.code.openperfmon.dao.MonitorInstanceDAO;
import com.google.code.openperfmon.db.DBManager;
import com.google.code.openperfmon.domain.Host;
import com.google.code.openperfmon.monapi.Icmp;
import com.google.code.openperfmon.monapi.PingResult;

public class HostService {
	private static Logger logger = Logger.getLogger(HostService.class);
	private HostDAO hostDAO;
	private MonitorInstanceDAO monitorInstanceDAO;
	
	HostService(HostDAO hostDAO, MonitorInstanceDAO instanceDAO) {
		this.hostDAO = hostDAO;
		monitorInstanceDAO = instanceDAO;
	}
	
	public List<AdminEntityInfo<Host>> listHosts() {
		return listHosts(0, Integer.MAX_VALUE);
	}

	public List<AdminEntityInfo<Host>> listHosts(int start, int count) {
		List<Host> hosts = hostDAO.list(start, count);
		if (hosts.isEmpty()) return Collections.emptyList();
		Collections.sort(hosts);
		
		Map<Long, Integer> cntById = monitorInstanceDAO.getInstanceCountsByHostIds(hostDAO.identities(hosts));
		List<AdminEntityInfo<Host>> hostRes = new ArrayList<AdminEntityInfo<Host>>(hosts.size());
		for (Host h : hosts) {
			AdminEntityInfo<Host> hostInfo = new AdminEntityInfo<Host>(h, true);
			Integer n = cntById.get(h.getId());
			int cnt = (n == null) ? 0 : n.intValue();
			hostInfo.setRefCount(cnt);
			hostRes.add(hostInfo);
		}
		return hostRes;
	}
	
	public Host getById(Long id) {
		return hostDAO.getById(id);
	}
	
	public void deleteHost(final Long id) {
		DBManager.transact(new DBManager.Do() {
			public void perform(Session session) {
				monitorInstanceDAO.deleteByHostId(id);
				hostDAO.removeById(id);
			}
		});
	}
	
	public void saveHost(final Host host) throws NonUniqueFieldException {
		try {
			hostDAO.save(host);
		}
		catch (ConstraintViolationException e) {
			Host existing = hostDAO.getByName(host.getName());
			if (existing == null) {
				String msg = "Can't save host named '" + host.getName() + "' due to unknown reason";
				logger.error(msg, e);
				throw new IllegalStateException(msg, e);
			}
			else {
				throw new NonUniqueFieldException("Host named '" + host.getName() + "' already exist", e, "name"); 
			}
		}
	}
	
	public HostTestingResult testHost(String host) {
		HostTestingResult res = new HostTestingResult();
		try {
			InetAddress addr = InetAddress.getByName(host);
			res.setResolved(true);
			PingResult ping = Icmp.instance.ping(addr, 3, 10000);
			res.setReached(ping.isAlive());
			if (ping.isAlive()) res.setReachTime(ping.getAverageTime());
		} 
		catch (UnknownHostException e) {
			res.setResolved(false);
		}
		
		return res;
	}
}
